1 using System;
2 using UnityEngine;
3
4 namespace UnityStandardAssets.ImageEffects
5 {
6 [ExecuteInEditMode]
7 [RequireComponent (typeof(Camera))]
8 [AddComponentMenu("Image Effects/Rendering/Screen Space Ambient Occlusion")]
9 public class ScreenSpaceAmbientOcclusion : MonoBehaviour
10 {
11 public enum SSAOSamples
12 {
13 Low = 0,
14 Medium = 1,
15 High = 2,
16 }
17
18 [Range(0.05f, 1.0f)]
19 public float m_Radius = 0.4f;
20 public SSAOSamples m_SampleCount = SSAOSamples.Medium;
21 [Range(0.5f, 4.0f)]
22 public float m_OcclusionIntensity = 1.5f;
23 [Range(0, 4)]
24 public int m_Blur = 2;
25 [Range(1,6)]
26 public int m_Downsampling = 2;
27 [Range(0.2f, 2.0f)]
28 public float m_OcclusionAttenuation = 1.0f;
29 [Range(0.00001f, 0.5f)]
30 public float m_MinZ = 0.01f;
31
32 public Shader m_SSAOShader;
33 private Material m_SSAOMaterial;
34
35 public Texture2D m_RandomTexture;
36
37 private bool m_Supported;
38
39 private static Material CreateMaterial (Shader shader)
40 {
41 if (!shader)
42 return null;
43 Material m = new Material (shader);
44 m.hideFlags = HideFlags.HideAndDontSave;
45 return m;
46 }
47 private static void DestroyMaterial (Material mat)
48 {
49 if (mat)
50 {
51 DestroyImmediate (mat);
52 mat = null;
53 }
54 }
55
56
57 void OnDisable()
58 {
59 DestroyMaterial (m_SSAOMaterial);
60 }
61
62 void Start()
63 {
64 if (!SystemInfo.supportsImageEffects || !SystemInfo.SupportsRenderTextureFormat (RenderTextureFormat.Depth))
65 {
66 m_Supported = false;
67 enabled = false;
68 return;
69 }
70
71 CreateMaterials ();
72 if (!m_SSAOMaterial || m_SSAOMaterial.passCount != 5)
73 {
74 m_Supported = false;
75 enabled = false;
76 return;
77 }
78
79 //CreateRandomTable (26, 0.2f);
80
81 m_Supported = true;
82 }
83
84 void OnEnable () {
85 GetComponent<Camera>().depthTextureMode |= DepthTextureMode.DepthNormals;
86 }
87
88 private void CreateMaterials ()
89 {
90 if (!m_SSAOMaterial && m_SSAOShader.isSupported)
91 {
92 m_SSAOMaterial = CreateMaterial (m_SSAOShader);
93 m_SSAOMaterial.SetTexture ("_RandomTexture", m_RandomTexture);
94 }
95 }
96
97 [ImageEffectOpaque]
98 void OnRenderImage (RenderTexture source, RenderTexture destination)
99 {
100 if (!m_Supported || !m_SSAOShader.isSupported) {
101 enabled = false;
102 return;
103 }
104 CreateMaterials ();
105
106 m_Downsampling = Mathf.Clamp (m_Downsampling, 1, 6);
107 m_Radius = Mathf.Clamp (m_Radius, 0.05f, 1.0f);
108 m_MinZ = Mathf.Clamp (m_MinZ, 0.00001f, 0.5f);
109 m_OcclusionIntensity = Mathf.Clamp (m_OcclusionIntensity, 0.5f, 4.0f);
110 m_OcclusionAttenuation = Mathf.Clamp (m_OcclusionAttenuation, 0.2f, 2.0f);
111 m_Blur = Mathf.Clamp (m_Blur, 0, 4);
112
113 // Render SSAO term into a smaller texture
114 RenderTexture rtAO = RenderTexture.GetTemporary (source.width / m_Downsampling, source.height / m_Downsampling, 0);
115 float fovY = GetComponent<Camera>().fieldOfView;
116 float far = GetComponent<Camera>().farClipPlane;
117 float y = Mathf.Tan (fovY * Mathf.Deg2Rad * 0.5f) * far;
118 float x = y * GetComponent<Camera>().aspect;
119 m_SSAOMaterial.SetVector ("_FarCorner", new Vector3(x,y,far));
120 int noiseWidth, noiseHeight;
121 if (m_RandomTexture) {
122 noiseWidth = m_RandomTexture.width;
123 noiseHeight = m_RandomTexture.height;
124 } else {
125 noiseWidth = 1; noiseHeight = 1;
126 }
127 m_SSAOMaterial.SetVector ("_NoiseScale", new Vector3 ((float)rtAO.width / noiseWidth, (float)rtAO.height / noiseHeight, 0.0f));
128 m_SSAOMaterial.SetVector ("_Params", new Vector4(
129 m_Radius,
130 m_MinZ,
131 1.0f / m_OcclusionAttenuation,
132 m_OcclusionIntensity));
133
134 bool doBlur = m_Blur > 0;
135 Graphics.Blit (doBlur ? null : source, rtAO, m_SSAOMaterial, (int)m_SampleCount);
136
137 if (doBlur)
138 {
139 // Blur SSAO horizontally
140 RenderTexture rtBlurX = RenderTexture.GetTemporary (source.width, source.height, 0);
141 m_SSAOMaterial.SetVector ("_TexelOffsetScale",
142 new Vector4 ((float)m_Blur / source.width, 0,0,0));
143 m_SSAOMaterial.SetTexture ("_SSAO", rtAO);
144 Graphics.Blit (null, rtBlurX, m_SSAOMaterial, 3);
145 RenderTexture.ReleaseTemporary (rtAO); // original rtAO not needed anymore
146
147 // Blur SSAO vertically
148 RenderTexture rtBlurY = RenderTexture.GetTemporary (source.width, source.height, 0);
149 m_SSAOMaterial.SetVector ("_TexelOffsetScale",
150 new Vector4 (0, (float)m_Blur/source.height, 0,0));
151 m_SSAOMaterial.SetTexture ("_SSAO", rtBlurX);
152 Graphics.Blit (source, rtBlurY, m_SSAOMaterial, 3);
153 RenderTexture.ReleaseTemporary (rtBlurX); // blurX RT not needed anymore
154
155 rtAO = rtBlurY; // AO is the blurred one now
156 }
157
158 // Modulate scene rendering with SSAO
159 m_SSAOMaterial.SetTexture ("_SSAO", rtAO);
160 Graphics.Blit (source, destination, m_SSAOMaterial, 4);
161
162 RenderTexture.ReleaseTemporary (rtAO);
163 }
164
165 /*
166 private void CreateRandomTable (int count, float minLength)
167 {
168 Random.seed = 1337;
169 Vector3[] samples = new Vector3[count];
170 // initial samples
171 for (int i = 0; i < count; ++i)
172 samples[i] = Random.onUnitSphere;
173 // energy minimization: push samples away from others
174 int iterations = 100;
175 while (iterations-- > 0) {
176 for (int i = 0; i < count; ++i) {
177 Vector3 vec = samples[i];
178 Vector3 res = Vector3.zero;
179 // minimize with other samples
180 for (int j = 0; j < count; ++j) {
181 Vector3 force = vec - samples[j];
182 float fac = Vector3.Dot (force, force);
183 if (fac > 0.00001f)
184 res += force * (1.0f / fac);
185 }
186 samples[i] = (samples[i] + res * 0.5f).normalized;
187 }
188 }
189 // now scale samples between minLength and 1.0
190 for (int i = 0; i < count; ++i) {
191 samples[i] = samples[i] * Random.Range (minLength, 1.0f);
192 }
193
194 string table = string.Format ("#define SAMPLE_COUNT {0}\n", count);
195 table += "const float3 RAND_SAMPLES[SAMPLE_COUNT] = {\n";
196 for (int i = 0; i < count; ++i) {
197 Vector3 v = samples[i];
198 table += string.Format("\tfloat3({0},{1},{2}),\n", v.x, v.y, v.z);
199 }
200 table += "};\n";
201 Debug.Log (table);
202 }
203 */
204 }
205 }